package org.streets.eis.component.combobox;

import java.util.ArrayList;
import java.util.Iterator;

import org.apache.wicket.Application;
import org.apache.wicket.RequestContext;
import org.apache.wicket.Response;
import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.WicketAjaxReference;
import org.apache.wicket.extensions.ajax.markup.html.autocomplete.AutoCompleteBehavior;
import org.apache.wicket.extensions.ajax.markup.html.autocomplete.AutoCompleteSettings;
import org.apache.wicket.extensions.ajax.markup.html.autocomplete.IAutoCompleteRenderer;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.html.IHeaderContributor;
import org.apache.wicket.markup.html.IHeaderResponse;
import org.apache.wicket.markup.html.WicketEventReference;
import org.apache.wicket.markup.html.form.AbstractTextComponent;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.IOnChangeListener;
import org.apache.wicket.markup.html.resources.JavascriptResourceReference;
import org.apache.wicket.markup.html.tree.BaseTree;
import org.apache.wicket.model.IModel;
import org.apache.wicket.protocol.http.WebRequest;
import org.apache.wicket.protocol.http.portlet.PortletRequestContext;
import org.apache.wicket.settings.IDebugSettings;
import org.apache.wicket.util.string.Strings;

import org.streets.eis.component.AjaxFormComponentUpdatedBehavior;
import org.streets.eis.component.ChangedNotifyType;
import org.streets.eis.component.model.ListItem;

/**
 * 
 * 注意：html form的reset动作对此控件可能无效（在getChangedNotifyType返回不为NONE时）
 */
public abstract class ComboBox<T> extends AbstractTextComponent<ListItem> implements IOnChangeListener,IHeaderContributor{
	private static final long serialVersionUID = 1L;

	private AutoCompleteSettings settings;	 
	
	public static final String CODEID_SUFFIX = "_$Id";
	
	private boolean canInput = true;
	
	private T dataModel;
	
	public ComboBox(String id, IModel<ListItem> model,T dataModel,boolean canInput){
		this(id,model,dataModel,canInput,null);
	}
	
	public ComboBox(String id, IModel<ListItem> model,T dataModel,boolean canInput, AutoCompleteSettings settings){
		super(id, model);
		setType(ListItem.class);
		this.settings = settings;
		this.dataModel = dataModel;
		this.canInput = canInput;
		init();
	}
	
	public ComboBox(String id,T dataModel,boolean canInput){
		this(id,dataModel,canInput,null);
	}
	
	public ComboBox(String id,T dataModel,boolean canInput, AutoCompleteSettings settings){
		super(id);
		setType(ListItem.class);
		this.settings = settings;
		this.dataModel = dataModel;
		this.canInput = canInput;
		init();
	}
	
	private void init(){
		if (settings==null){
			this.settings = newDefaultSettings();
		}
		
		add(newAutoCompleteBehavior(newAutoCompleteRenderer(), settings));
		add(newSelectorBahavior());
		
		if (getChangedNotifyType()==ChangedNotifyType.AJAX){
			add(new AjaxFormComponentUpdatedBehavior("onchange"){
				private static final long serialVersionUID = 1L;
				@Override
				protected CharSequence getPostData(){
					return "\""+getInputName() +	"=\" + Wicket.Form.encode(this.value) + \"&" + 
						getCodeFieldId()+ "=\" + Wicket.Form.encode(document.getElementById('"+getCodeFieldMarkupId()+"').value)";
				}
				@Override
				protected void onUpdate(AjaxRequestTarget target) {
					ComboBox.this.onAjaxItemChanged(ComboBox.this.getModelObject(),target);
				}
			});
		}
	}
	
	protected SelectorBehavior newSelectorBahavior(){
		return new SelectorBehavior(){
			private static final long serialVersionUID = 1L;
			@Override
			protected void onIconClicked(Response response){
				ComboBox.this.onIconClicked(response);
			}
			@Override
			protected void onIconClicked(AjaxRequestTarget target){
				ComboBox.this.onNoPopup(target);
			}
			@Override
			protected boolean canShowPopup(){
				return ComboBox.this.canShowPopup();
			}
		};
	}
		
	protected AutoCompleteSettings newDefaultSettings(){
		AutoCompleteSettings settings = new AutoCompleteSettings();
		settings.setMaxHeightInPx(200);
		settings.setShowListOnEmptyInput(true);
		return settings;
	}
	
	protected String getItemIcon(ListItem item,boolean useDefault){
		if (item.isNull()) return "";
		String icon = item.getIcon();
		String contextPath = ((WebRequest)getRequest()).getHttpServletRequest().getContextPath();
		if (icon!=null&&icon.length()>0){
			if (icon.startsWith("/")){ //web目录下的
				icon = contextPath + icon;
			}else{
				icon = contextPath + "/resources/" + icon;
			}			
		} else{
			if (useDefault) {
				icon = contextPath + "/resources/" + BaseTree.class.getName()+"/res/item.gif";
			}
		}
		return icon;
	}
	
	protected void renderItem(ListItem item,Response response){
		String textValue = item.getText();
		if (textValue==null||textValue.trim().length()==0) textValue = "&nbsp;";
		textValue = textValue.trim();
		String id = item.getId();	
		
        response.write("<li i=\""+id+"\">");
        
        response.write("<a style=\"width:16px;height:16px;vertical-align:middle;background: url(");
		response.write(getItemIcon(item,true));
		response.write(") left center no-repeat\" />");

		response.write("<a>");
		response.write(textValue);
		response.write("</a>");
		
		response.write("</li>");
	}
	
	protected IAutoCompleteRenderer<ListItem> newAutoCompleteRenderer(){
		return new IAutoCompleteRenderer<ListItem>(){
			private static final long serialVersionUID = 1L;

			public final void render(ListItem object, Response response, String criteria){
				ComboBox.this.renderItem(object,response);
			}
			
			public void renderHeader(Response response) {
				response.write("<ul>");
			}

			public void renderFooter(Response response) {
				response.write("</ul>");				
			}
		};
	}

	protected AutoCompleteBehavior<ListItem> newAutoCompleteBehavior(IAutoCompleteRenderer<ListItem> renderer,
		AutoCompleteSettings settings)	{
		return new AutoCompleteBehavior<ListItem>(renderer, settings)	{
			private static final long serialVersionUID = 1L;
			
			@Override
			public void renderHead(IHeaderResponse response){
				final IDebugSettings debugSettings = Application.get().getDebugSettings();

				response.renderJavascriptReference(WicketEventReference.INSTANCE);
				response.renderJavascriptReference(WicketAjaxReference.INSTANCE);

				if (debugSettings.isAjaxDebugModeEnabled()){
					response.renderJavascriptReference(new JavascriptResourceReference(AbstractDefaultAjaxBehavior.class, "wicket-ajax-debug.js"));
					response.renderJavascript("wicketAjaxDebugEnable=true;", "wicket-ajax-debug-enable");
				}

				RequestContext context = RequestContext.get();
				if (context.isPortletRequest()){
					response.renderJavascript("Wicket.portlet=true", "wicket-ajax-portlet-flag");
				}
				renderComboBoxHead(response);
			}
			
			private void renderComboBoxHead(IHeaderResponse response){
				String indicatorId = findIndicatorId();
				if (Strings.isEmpty(indicatorId)){
					indicatorId = "null";
				}else	{
					indicatorId = "'" + indicatorId + "'";
				}
				String initJs = ComboBox.this.getInitJavascript(getCallbackUrl(),constructSettingsJS(), indicatorId);
				response.renderOnDomReadyJavascript(initJs);
			}
			
			@Override
			protected Iterator<ListItem> getChoices(String input)	{
				if (!Strings.isEmpty(input)) input = input.replaceAll("'", "''");
				return ComboBox.this.getChoices(input);
			}
		};
	}

	@Override
	protected void onRender(final MarkupStream markupStream){	
		super.onRender(markupStream);
		String text = getCodeTextValue().replace("\"", "&#34;");
		StringBuilder buffer = new StringBuilder();
		buffer.append("<span style=\"display:none\"><input type=\"hidden\" name=\"").append(getCodeFieldId())
			.append("\" id=\"").append(getCodeFieldMarkupId()).append("\" value=\"").append(getCodeIdValue()).append("\" ")
			.append(" i=\"").append(getCodeIdValue()).append("\"").append(" t=\"").append(text).append("\"")
			.append(" /></span>");

		getResponse().write(buffer);			
	}

	@Override
	protected void onComponentTag(ComponentTag tag){
		checkComponentTag(tag, "input");
		if (tag.getAttributes().containsKey("type")){
			checkComponentTagAttribute(tag, "type", "text");
		} else {
			tag.put("type", "text");
		}
		if (!canInput) {
			tag.put("readonly", "readonly");
		}
		if (isRequired()) {
			tag.put("r", "1");
		}
		tag.put("value", getModelValue());		
		tag.put("autocomplete", "off");
		
		String title = tag.getAttribute("title");
		if (!Strings.isEmpty(title)){
			tag.put("title", title);
		}
		if (getChangedNotifyType()==ChangedNotifyType.FORM){
			CharSequence url = urlFor(IOnChangeListener.INTERFACE);

			Form<?> form = findParent(Form.class);
			if (form != null){
				RequestContext rc = RequestContext.get();
				if (rc.isPortletRequest()){
					url = ((PortletRequestContext)rc).getLastEncodedPath();
				}
				tag.put("onchange", form.getJsForInterfaceUrl(url));
			} else {
				tag.put("onchange", "window.location.href='" + url +
						(url.toString().indexOf('?') > -1 ? "&amp;" : "?") + 
						getInputName() +	"=' + this.value + '&amp;" +
						getCodeFieldId()+ "=' + document.getElementById('"+getCodeFieldMarkupId()+"').value;");
			}
		}
		super.onComponentTag(tag);
	}

	protected String getCodeFieldId(){
		return getInputName() + CODEID_SUFFIX;
	}
	
	protected String getCodeFieldMarkupId(){
		return getMarkupId() + CODEID_SUFFIX;
	}	
	
	protected String getCodeIdValue(){
		ListItem model = getModelObject();
		return model!=null?model.getId() : "";
	}
	
	protected String getCodeTextValue(){
		ListItem model = getModelObject();
		return model!=null?model.getText() : "";
	}
	
	@Override
	public String getModelValue(){
		return getCodeTextValue();
	}

	@Override
	protected void convertInput(){
		String id = getRequest().getParameter(getCodeFieldId());
		String text = getRequest().getParameter(getInputName());
		setConvertedInput(new ListItem(id,text));
	}
	
	public void onSelectionChanged() {
		convertInput();
		updateModel();
		onItemChanged(getModelObject());
	}
	
	protected ChangedNotifyType getChangedNotifyType(){
		return ChangedNotifyType.NONE;
	}
	
	public T getDataModel(){
		return dataModel;
	}
	
	public ComboBox<T> setDataModel(T dataModel){
		this.dataModel = dataModel;
		return this;
	}
	
	protected Iterator<ListItem> getChoices(String key){
		if (!Strings.isEmpty(key) && key.indexOf("'")>=0){
			key = key.replaceAll("'", "''");
		}
		if (canInput) { 
			return getMatchedItems(key);
		} else {
			return new ArrayList<ListItem>().iterator();
		}
	}
		
	protected String generateLikeSql(String field,String value){
		if (value.indexOf("%")>=0){
			return field + " like '%" + value.replaceAll("%", "/%") + "%' escape '/' ";
		} else {
			return field + " like '%" + value + "%'";
		}
	}
	
	protected boolean canShowPopup(){
		return getDataModel()!=null;
	}
	
	protected void onNoPopup(AjaxRequestTarget target){		
	}

	protected abstract void onIconClicked(Response response);	
	
	protected abstract String getInitJavascript(CharSequence callbackUrl,String SettingsJS, String indicatorId) ;
	
	
	protected void onItemChanged(ListItem newValue){
	}
	
	/** 
	 * 对于要在此方法里使用ajax更新的控件，如果是类似ComboBox这种会生成多个平级html dom元素的（不是单html dom根的）
	 * 要在被更新的控件的wicket-component结构上加一个WebMarkupContainer控件并更新它
	 * 否则，weicket只会替换掉html dom id为其MarkupId的Element,其他元素会重复
	 */
	protected void onAjaxItemChanged(ListItem newValue,AjaxRequestTarget target){
	}

	protected abstract Iterator<ListItem> getMatchedItems(String key);
	
}
